package io.github.luons.mediator.generator.utils;

import io.github.luons.docs.node.FieldNode;
import io.github.luons.mediator.generator.entity.ParamConfig;
import io.github.luons.mediator.generator.entity.ParamNodeBean;
import io.github.luons.mediator.generator.entity.YmlConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * YmlUtils
 *
 * @author : luons
 */
@Slf4j
public class YmlUtils {

    /**
     * 数据接口路径参数
     */
    public static Map<String, ParamNodeBean> URL_PARAM_MAP = new HashMap<>();

    /**
     * 数据类型转换
     */
    public static Map<String, String> TRANSFORM_KEY = new HashMap<>();

    /**
     * 必须键转换
     */
    public static Map<String, Boolean> REQUIRED_KEY_MAP = new HashMap<>();

    /**
     * DTO Model
     */
    public static Map<String, Object> DTO_MODEL_MAP = new HashMap<>();

    /**
     * Long类型转换
     */
    public static final String LONG_KEY = "long";

    /**
     * 数组key值
     */
    public static final String ARRAY_KEY = "array";

    /**
     * 对象key值
     */
    public static final String OBJECT_KEY = "object";

    static {
        TRANSFORM_KEY.put("int", "integer");
        TRANSFORM_KEY.put("long", "integer");
        TRANSFORM_KEY.put("float", "number");
        TRANSFORM_KEY.put("double", "number");
        TRANSFORM_KEY.put("date", "string");
        TRANSFORM_KEY.put("enum", "string");
        TRANSFORM_KEY.put("_object", OBJECT_KEY);
        TRANSFORM_KEY.put("string[]", ARRAY_KEY);
        TRANSFORM_KEY.put("int[]", ARRAY_KEY);
        TRANSFORM_KEY.put("float[]", ARRAY_KEY);
        TRANSFORM_KEY.put("long[]", ARRAY_KEY);
        TRANSFORM_KEY.put("double[]", ARRAY_KEY);
        TRANSFORM_KEY.put("date[]", ARRAY_KEY);
    }

    static {
        REQUIRED_KEY_MAP.put("[非必须]", false);
        REQUIRED_KEY_MAP.put("【非必须】", false);
        REQUIRED_KEY_MAP.put("【必须】", true);
        REQUIRED_KEY_MAP.put("[必须]", true);
    }

    public static LinkedHashMap<String, Object> generateObjectMap(FieldNode node) {
        String type = node.getType();
        LinkedHashMap<String, Object> value = new LinkedHashMap<>();
        String transformType = transformType(type);
        value.put("type", transformType);
        value.put("description", node.getDescription());
        if (LONG_KEY.equals(type)) {
            value.put("format", "int64");
        }
        if (ARRAY_KEY.equals(transformType)) {
            Map<String, Object> items = new LinkedHashMap<>();
            items.put("type", type.replaceAll(YmlConstant.ARRAY_CLEAN, ""));
            value.put("items", items);
        }
        return value;
    }

    public static LinkedHashMap<String, Object> generateDtoMap(String filename, Map<String, Object> paramMap) {
        if (MapUtils.isEmpty(paramMap)) {
            return null;
        }
        LinkedHashMap<String, Object> data = new LinkedHashMap<>();
        if (MapUtils.isEmpty(paramMap)) {
            return data;
        }
        for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
            String key = entry.getKey();
            if (!StringUtils.isEmpty(key) && !Objects.isNull(entry.getValue())) {
                Map<String, Object> pMap = genRequestParam(new HashMap<>(), filename, key, "", entry.getValue());
                if (MapUtils.isNotEmpty(pMap)) {
                    data.put(key, pMap);
                }
            }
        }
        return data;
    }


    private static Map<String, Object> genRequestParam(Map<String, Object> dataMap, String filename, String key, String type, Object descObj) {
        if (descObj instanceof String) {
            return generatorRequestDesc(descObj.toString());
        } else if (descObj instanceof List) {
            List<Object> descObjList = (List<Object>) descObj;
            Object descObjData = descObjList.get(0);
            if (descObjData instanceof String) {
                return generatorRequestDesc(descObj.toString());
            } else if (descObjData instanceof Map) {
                return genRequestParam(dataMap, filename + "Rec", key + "Rec", ARRAY_KEY, descObjData);
            }
            return null;
        } else if (descObj instanceof Map) {
            // 内嵌的DTO，需要生成Model
            Map<String, Object> paramMap = (Map<String, Object>) descObj;
            for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
                dataMap.put(entry.getKey(), genRequestParam(new HashMap<>(paramMap.size()), filename, entry.getKey(), type, entry.getValue()));
            }
            return genModelMap(filename, key, type, key, dataMap);
        }
        return null;
    }

    private static Map<String, Object> generatorRequestDesc(String typeDescription) {
        String[] array = typeDescription.replace("/", "").split(" ");
        if (array.length < 1) {
            return null;
        }
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        String type = transformType(array[0].trim());
        map.put("type", type);
        // 如果是数组，需要设置其类型
        if (ARRAY_KEY.equals(type)) {
            Map<String, Object> typeMap = new HashMap<>();
            typeMap.put("type", transformType(array[0].trim().replaceAll("[\\[\\]]", "")));
            map.put("items", typeMap);
        }
        if (LONG_KEY.equals(array[0].trim())) {
            map.put("format", "int64");
        }
        String description = array.length == 1 ? ""
                : Arrays.stream(array, 1, array.length).collect(Collectors.joining());
        for (Map.Entry<String, Boolean> e : REQUIRED_KEY_MAP.entrySet()) {
            if (description.contains(e.getKey())) {
                map.put("required", REQUIRED_KEY_MAP.containsKey(e.getKey()));
                description = description.replace(e.getKey(), "");
                break;
            }
        }
        map.put("description", description);
        return map;
    }

    public static Map<String, Object> generateModelMap(Map<String, Object> pro, String title, String desc, boolean isList) {
        LinkedHashMap<String, Object> dtoProperties = new LinkedHashMap<>();
        LinkedHashMap<String, Object> properties = new LinkedHashMap<>();
        properties.put("x-openapi-module", ParamConfig.MODULE);
        properties.put("description", desc);
        properties.put("title", title);
        properties.put("type", isList ? ARRAY_KEY : OBJECT_KEY);
        properties.put("properties", pro);
        dtoProperties.put(title, properties);

        return dtoProperties;
    }

    public static String transformType(String type) {
        if (StringUtils.isEmpty(type)) {
            return "string";
        }
        type = type.replace("_", "");
        return TRANSFORM_KEY.getOrDefault(type.toLowerCase(), "string");
    }

    public static boolean ymlModel(String filePath, String filename, Map<String, Object> valueMap) {

        LinkedHashMap<String, Object> ymlMap = new LinkedHashMap<>();
        if (MapUtils.isEmpty(valueMap)) {
            return false;
        }
        filename = filename + ".yaml";
        filePath = filePath + File.separator + "model" + File.separator;
        ymlMap.put("swagger", YmlConstant.SWAGGER_VERSION);
        ymlMap.put("definitions", valueMap);

        ymlGenerator(filePath, filename, ymlMap);
        return true;
    }

    public static void ymlGenerator(String filePath, String filename, Map<String, Object> ymlMap) {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
        DumperOptions dumperOptions = new DumperOptions();
        dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        Yaml yaml = new Yaml(dumperOptions);
        try {
            FileWriter fileWriter = new FileWriter(filePath + filename);
            yaml.dump(ymlMap, fileWriter);
        } catch (IOException e) {
            log.error("{} {} ymlGenerator is error " + e, filePath, filename);
        }
    }

    public static Map<String, Object> genModelMap(String filename, String key, String type, String desc, Map<String, Object> dataMap) {
        // 生成model
        Map<String, Object> modelMap = new LinkedHashMap<>(4);
        Map<String, Object> filenameMap = new LinkedHashMap<>(1);
        modelMap.put("x-openapi-module", ParamConfig.MODULE);
        modelMap.put("title", key);
        modelMap.put("description", desc);
        modelMap.put("type", OBJECT_KEY);
        modelMap.put("properties", dataMap);
        filenameMap.put(key, modelMap);
        if (DTO_MODEL_MAP.containsKey(filename)) {
            ((Map<String, Object>) DTO_MODEL_MAP.get(filename)).putAll(filenameMap);
        } else {
            DTO_MODEL_MAP.put(filename, filenameMap);
        }
        LinkedHashMap<String, Object> schema = new LinkedHashMap<>();
        if (ARRAY_KEY.equalsIgnoreCase(type)) {
            LinkedHashMap<String, Object> items = new LinkedHashMap<>();
            items.put("$ref", MessageFormat.format(YmlConstant.BASE_PATH, filename, key));
            schema.put("items", items);
        } else {
            schema.put("$ref", MessageFormat.format(YmlConstant.BASE_PATH, filename, key));
        }
        if (StringUtils.isNotBlank(type)) {
            schema.put("type", type);
        }
        schema.put("description", desc);
        schema.put("required", isRequired(dataMap));

        return schema;
    }

    private static boolean isRequired(Map<String, Object> dataMap) {
        if (dataMap.containsKey("required") && dataMap.get("required").equals(true)) {
            return true;
        }
        return dataMap.values().stream().anyMatch(o -> o instanceof Map && isRequired((Map<String, Object>) o));
    }

}
